import java.util.Random;

/**
 * 
 * @author Rony Lahav 043489889
 *
 */

public class Germ {

	private int locX;		//matrix X axis location
	private int locY;		//matrix Y axis location
	private int adventure;	//the adventure level of the Germ
	private float energy;	//the energy level of the germ
	private int age;		//how many steps did the germ take
	private int direction;	//in which direction does the germ's face face
	
	//default CTOR for a randomly created germ
	public Germ(float E){
		//a new germ can't be initialized to a location where there's already another germ
		Random rand=new Random(System.currentTimeMillis());
		do{
			//get a random first location for the germ
			locX = rand.nextInt(GermsMatrix.N);
			locY = rand.nextInt(GermsMatrix.N);
		}
		while (GermsMatrix.germsMatrix[locX][locY]==1);
		direction=rand.nextInt(4);	//randomly choose to which direction the germ faces
		energy = E;
		age=0;
		adventure = rand.nextInt(10);	//adventure rate between 0-9
		GermsMatrix.germsMatrix[locX][locY]=1;	//update that there's a germ in location [x,y] in the matrix
		eatFood();	//if we got lucky we eat the food
	}
	
	//CTOR for creating a splitted germ
	public Germ(float E, int locX, int locY, int adventure, int direction){
		Random rand=new Random(System.currentTimeMillis());
		//init a specific location in the matrix
		this.locX = locX;
		this.locY = locY;
		energy = E;
		age=0;
		this.adventure = adventure;	//adventure rate between 0-9 coming from the father
		//randomly allow an adventure rate mutation - the higher it is the less chance for change
		if ((float)(1/adventure)>rand.nextFloat())
			this.adventure+=1;	//mutation upwards
		else
			if ((float)(1/adventure)>rand.nextFloat())
				this.adventure-=1;	//mutation downward
		//make sure we are still in 0-9 bounds
		if (this.adventure>9)
			--this.adventure;
		else
			if(this.adventure<0)
				++this.adventure;
		this.direction=direction;
		GermsMatrix.germsMatrix[locX][locY]=1;	//update that there's a germ in location [x,y] in the matrix
		eatFood();	//if we got lucky we eat the food
	}
	
	//updates the current energy to 1/2 and returns a duplicate Germ (a bit like CCTOR)
	public Germ splitAndCopy(){
		Germ newGerm=null;
		int direction = 1;	//will get the direction to which it was created relatively to the existing germ
		//find a location for the new germ, and if there's no free space around the existing one the split is canceled
		int nextX=locX, nextY=locY;
		if (nextY+1==GermsMatrix.N)
			nextY=0;
		else
			nextY+=1;
		//no space to the right - check left
		if (GermsMatrix.germsMatrix[nextX][nextY]==1){
			nextX=locX;
			nextY=locY;
			if (nextY-1<0)
				nextY=GermsMatrix.N-1;
			else
				nextY-=1;
			direction = 3;
			//no space to the left - check up
			if (GermsMatrix.germsMatrix[nextX][nextY]==1){
				nextX=locX;
				nextY=locY;
				if (nextX-1<0)
					nextX=GermsMatrix.N-1;
				else
					nextX-=1;
				direction = 0;
				//no space upwards - check down
				if (GermsMatrix.germsMatrix[nextX][nextY]==1){
					nextX=locX;
					nextY=locY;
					if (nextX+1==GermsMatrix.N)
						nextX=0;
					else
						nextX+=1;
					direction = 2;
					//no space downward => no space for the new germ
					if (GermsMatrix.germsMatrix[nextX][nextY]==1){
						nextX=locX;
						nextY=locY;
					}
				}
			}
		}
		//if we have a place for the new germ
		if (nextX!=locX && nextY!=locY){
			energy/=2;
			newGerm = new Germ(energy, nextX, nextY, adventure, direction);
		}
		//if new germ wasn't created we return null
		return newGerm;
	}
	
	/* The germ decides if he turns or goes in the same direction, updates its location accordingly,
	 * and then checks if he landed on a place with food.
	 * His age and energy levels are then updated accordingly,
	 * and the matrix loses the food at the location because it got eaten
	 */
	public void performStep(){
		//if we are still alive
		if (energy>0){
			if (adventure!=0){
				Random rand = new Random(System.currentTimeMillis());
				//the bigger the adventure the higher chance for success
				if ((float)(1/adventure)<rand.nextFloat()){
					int direction = rand.nextInt(2);
					if (direction==0)	//change direction 90 degrees left
						--direction;
					else				//change direction 90 degrees right
						++direction;
				}
				if (direction<0)	//circular rotation (modulo 4)
					direction=3;
				else
					if (direction>3)
						direction=0;
			}
			switch (direction) {
			case 0:
				moveUp();
				break;
			case 1:
				moveRight();
				break;
			case 2:
				moveDown();
				break;
			case 3:
				moveLeft();
				break;
			default:
				break;
			}
			eatFood();
		}
		++age;
	}
	
	/* Checks if the germ is in the fluent food area, if so reports his energy with "1",
	 * else reports his energy with "0"
	 */
	public float[] reportData(){
		float[] data = new float[3];
		//check if we are in the fluent food area
		if (locX>=GermsMatrix.locMUp[0] && locX<=GermsMatrix.locMDown[0] && locY>=GermsMatrix.locMUp[1] && locY<=GermsMatrix.locMDown[1])
			data[0]=1;
		else
			data[0]=0;
		data[1]=energy;
		data[2]=adventure;
		return data;
	}
	
	//performs a step from location [x,y] to [x,y+1], where if y+1==N => y=0
	private void moveRight(){
		int nextX = locX;
		int nextY = locY+1;
		if (nextY==GermsMatrix.N)
			nextY=0;
		//if the destination is free of another germ
		if (GermsMatrix.germsMatrix[nextX][nextY]!=1){
			locX=nextX;
			locY=nextY;
		}
	}
	
	//performs a step from location [x,y] to [x,y-1], where if y-1<N => y=N-1
	private void moveLeft(){
		int nextX = locX;
		int nextY = locY-1;
		if (nextY<0)
			nextY=GermsMatrix.N-1;
		//if the destination is free of another germ
		if (GermsMatrix.germsMatrix[nextX][nextY]!=1){
			locX=nextX;
			locY=nextY;
		}
	}
	
	//if we adventure upwards we move from location [x,y] to [x-1,y], where if x-1<0 => x=N-1
	private void moveUp(){
		int nextX = locX-1;
		int nextY = locY;
		if (nextX<0)
			nextX=GermsMatrix.N-1;
		//if the destination is free of another germ
		if (GermsMatrix.germsMatrix[nextX][nextY]!=1){
			locX=nextX;
			locY=nextY;
		}
	}
	
	//if we adventure downwards we move from location [x,y] to [x+1,y], where if x+1==N => x=0 
	private void moveDown(){
		int nextX = locX+1;
		int nextY = locY;
		if (nextX==GermsMatrix.N)
			nextX=0;
		//if the destination is free of another germ
		if (GermsMatrix.germsMatrix[nextX][nextY]!=1){
			locX=nextX;
			locY=nextY;
		}
	}
	
	//if there's food in current location we eat it
	private void eatFood() {
		if (GermsMatrix.foodMatrix[locX][locY]==1){	//we got food
			GermsMatrix.foodMatrix[locX][locY]=0;	//eat it
			/* Increase your energy if you can.
			 * We give twice the energy we spend on a step when eating*/
			if (energy+0.2<=GermsMatrix.maxEnergy)
				energy+=0.2;
			else
				if (energy+0.1<=GermsMatrix.maxEnergy)
					energy+=0.1;
				
		}
		else	//we didn't find food so energy level decreases after step
			energy-=0.1;
	}
	
	//return true if the germ is qualified to split, false if not
	public boolean isQualifiedToSplit(){
		if (energy>=GermsMatrix.minSplitE && age>=GermsMatrix.splitAge)
			return true;
		else
			return false;
	}
	
	//returns true if germ has 0 energy and should die, false if not
	public boolean isDeadGerm(){
		return energy<=0;
	}
}
